﻿using System;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using System.Threading;

namespace LitTools
{
    /// <summary>
    /// 网络时间工具
    /// </summary>
    public class NetworkTimeUtility
    {
        /// <summary>
        /// NTP服务器地址
        /// </summary>
        //public readonly static string[] NTPServers = { "ntp.ntsc.ac.cn", "ntp1.aliyun.com", "ntp.sjtu.edu.cn" };
        public readonly static string[] NTPServers = { "pool.ntp.org", "time.windows.com", "time.apple.com" ,"time.google.com"};

        /// <summary>
        /// 最初时间
        /// </summary>
        public readonly static DateTime OriginTime = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        private const int port = 123;
        private const int timeout = 5;

        /// <summary>
        /// 获取网络时间
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="timeout"></param>
        private static Task<DateTime> GetNetworkTime(string server)
        {
            return Task.Run(() =>
            {
                try
                {
                    var ntpData = new byte[48];
                    ntpData[0] = 0x1B;

                    var addresses = Dns.GetHostEntry(server).AddressList;
                    var ipEndPoint = new IPEndPoint(addresses[0], port);

                    using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
                    {
                        socket.Connect(ipEndPoint);
                        socket.Send(ntpData);
                        socket.Receive(ntpData);
                        socket.Close();
                    }

                    ulong intPart = (ulong)ntpData[40] << 24 | (ulong)ntpData[41] << 16 | (ulong)ntpData[42] << 8 | (ulong)ntpData[43];
                    ulong fractPart = (ulong)ntpData[44] << 24 | (ulong)ntpData[45] << 16 | (ulong)ntpData[46] << 8 | (ulong)ntpData[47];

                    ulong milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
                    DateTime networkDateTime = OriginTime.AddMilliseconds((long)milliseconds);
                    return networkDateTime.ToLocalTime();
                }
                catch (Exception e)
                {
                    UnityEngine.Debug.LogError("Failed to get time from NTP server " + server + ": " + e.Message);
                    return DateTime.MinValue;
                }
            });
        }

        public static async void GetNetworkTime(Action<bool, DateTime> callback)
        {
            for (int i = 0; i < NTPServers.Length; i++)
            {
                DateTime time = await GetNetworkTime(NTPServers[i]);
                if (time != DateTime.MinValue)
                {
                    callback?.Invoke(true, time);
                    return;
                }
            }
            callback?.Invoke(false, DateTime.MinValue);
        }
    }
}
